import de.siegmar.fastcsv.reader.CsvContainer;
import de.siegmar.fastcsv.reader.CsvReader;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.referencing.CRS;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import utilityTools.CustomizeType;
import utilityTools.ShpReaderUtil;

import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class mydelete {

    public String openCSVWithExcelMode(String pathCSV, String xField, String yField, String enCode, String crsCode) throws Exception {
        //读取csv到内存
        File file = new File(pathCSV);
        String csvName = file.getName();
        CsvReader csvReader = new CsvReader();
        csvReader.setSkipEmptyRows(true);//跳过空行
        csvReader.setContainsHeader(true);//指定：第一行是否是作为表头，如果作为表头，那么下面的getRows时就不会获取该行；否则会将第一行作为普通数据
        CsvContainer csvContents = csvReader.read(file, Charset.forName(enCode));
        List<String> csvHeaders = csvContents.getHeader();//获取列标头
        for (String csvHeader : csvHeaders) {
            System.out.println(csvHeader);
        }
        Map<String, Object> checkResult = isCSVContentsIllegal(csvContents, xField, yField);
        if (((Boolean) checkResult.get("illegal"))) {
            //如果文件参数有问题，那么coordinates键存储的是问题原因;否则是转换后的xy坐标
            throw new IllegalArgumentException(checkResult.get("coordinates").toString());
        }
        double[][] xyConverted = (double[][]) checkResult.get("coordinates");
        //创建结果shp文件(保存在默认工作空间)
        EnumMap<CustomizeType, Object> shpMapEnum = ShpReaderUtil.createNewShp(csvName, csvHeaders,
                Point.class, CRS.decode(crsCode, true));
        String shpPath = shpMapEnum.get(CustomizeType.ShpPath).toString();
        ShapefileDataStore sfd = (ShapefileDataStore) shpMapEnum.get(CustomizeType.ShpDataStore);
        sfd.setCharset(Charset.forName(enCode));
//        System.out.println("方式1："+sfd.getSchema().getTypeName());
//        System.out.println("方式2："+sfd.getTypeNames()[0]);
        FeatureWriter<SimpleFeatureType, SimpleFeature> shpWriter = sfd.getFeatureWriter(sfd.getTypeNames()[0], Transaction.AUTO_COMMIT);
        //开始写入数据到shp文件中
        for (AttributeDescriptor attributeDescriptor : sfd.getSchema().getAttributeDescriptors()) {
            System.out.println(attributeDescriptor.getLocalName());
        }
        GeometryFactory geometryFactory =new GeometryFactory();//GeometryFactory必须和shp文件保证统一
        if (geometryFactory==null) {
            System.out.println("无法获取GeometryFactory对象");
            return "执行错误";
        }else {
            System.out.println("能够进入创建几何的一步");
        }
        int rowCnts = csvContents.getRowCount();
        for (int idxRow = 0; idxRow < rowCnts; idxRow++) {
            //创建几何
            Point pntNew = geometryFactory.createPoint(new Coordinate(xyConverted[idxRow][0], xyConverted[idxRow][1]));
            SimpleFeature featureNew = shpWriter.next();
            //几何列赋值
            featureNew.setDefaultGeometry(pntNew);
            //属性列赋值(一定要跳过几何列，否则几何列的值会被覆盖)
            for (String perField : csvHeaders) {
                featureNew.setAttribute(perField, csvContents.getRow(idxRow).getField(perField));
            }
            shpWriter.write();
        }
        shpWriter.close();//释放shp读写器
        //如果后期有要求，再增加geojson的结果类型
        return shpPath;
    }

    /**
     * 判断csv文件内容是否illegal
     *
     * @param csvContents csv内容对象
     * @param xField
     * @param yField
     * @return 键值对：true-表示内容存在问题，此时coordinates存储问题原因；
     * false-内容合法，此时coordinates存储转换后的坐标
     */
    private Map<String, Object> isCSVContentsIllegal(CsvContainer csvContents, String xField, String yField) {
        List<String> csvHeaders = csvContents.getHeader();
        Map<String, Object> checkContents = new HashMap<>();
        //检查是否具有指定的x和y字段
        if (!csvHeaders.contains(xField) || !csvHeaders.contains(yField)) {
            checkContents.put("illegal", true);//指示文件是否合规
            checkContents.put("coordinates", "指定的坐标字段不存在，请检查！");
        } else {
            int rowCnts = csvContents.getRowCount();
            double[][] coords = new double[rowCnts][2];//n行2列
            for (int idxRow = 0; idxRow < rowCnts; idxRow++) {
                if (!isNumber(csvContents.getRow(idxRow).getField(xField))) {
                    checkContents.put("isIllegal", true);//指示文件是否合规
                    checkContents.put("coordinates", "坐标X字段存在非数字型字符，请检查！");
                    return checkContents;
                } else if (!isNumber(csvContents.getRow(idxRow).getField(yField))) {
                    checkContents.put("isIllegal", true);//指示文件是否合规
                    checkContents.put("coordinates", "坐标Y字段存在非数字型字符，请检查！");
                    return checkContents;
                } else {
                    double xValue = Double.parseDouble(csvContents.getRow(idxRow).getField(xField));
                    double yValue = Double.parseDouble(csvContents.getRow(idxRow).getField(yField));
                    coords[idxRow] = new double[]{xValue, yValue};
                }
            }
            checkContents.put("illegal", false);//指示文件是否是‘非法的’
            checkContents.put("coordinates", coords);
        }
        return checkContents;
    }

    /**
     * 判断一个字符串是否是数字类型
     *
     * @param valueInput 待判断字符串
     * @return true-是合法的数字字符串
     */
    private boolean isNumber(String valueInput) {
        // 该正则表达式可以匹配所有的数字 包括负数
        Pattern pattern = Pattern.compile("-?[0-9]+(\\.[0-9]+)?");
        Matcher isNum = pattern.matcher(valueInput);// matcher是全匹配，即一个字符串必须全部是数字，不能包含其他字符
        return isNum.matches();
    }
}
